#include "tmodbus.h"

#include <stdio.h>
#include <string.h>

#define TMODBUS_SOFT_RELEASE_VERSION 0X0003  // 该版本是发行版本，并不是软件提交的版本

/***************************************************************************************************
 * ***********************************获取设备表数据宏定义开始************************************ *
 ***************************************************************************************************/
#define dev_table_device_address(dev_tab_idx_d)     (modbus_dev_table_s[dev_tab_idx_d].device_address)
#define dev_table_data_table(dev_tab_idx_d)         (modbus_dev_table_s[dev_tab_idx_d].data_table)
#define dev_table_send_buffer(dev_tab_idx_d)        (modbus_dev_table_s[dev_tab_idx_d].send_buffer)
#define dev_table_send_buffer_len(dev_tab_idx_d)    (modbus_dev_table_s[dev_tab_idx_d].send_buffer_len)
#define dev_table_frame_data_max_len(dev_tab_idx_d) (modbus_dev_table_s[dev_tab_idx_d].frame_data_max_len)

#define data_table_switch_table(dev_tab_idx_d)        (modbus_dev_table_s[dev_tab_idx_d].data_table->switch_table)
#define data_table_switch_table_size(dev_tab_idx_d)   (modbus_dev_table_s[dev_tab_idx_d].data_table->switch_table_len)
#define data_table_register_table(dev_tab_idx_d)      (modbus_dev_table_s[dev_tab_idx_d].data_table->register_table)
#define data_table_register_table_size(dev_tab_idx_d) (modbus_dev_table_s[dev_tab_idx_d].data_table->register_table_len)

#define swt_table_name(dev_tab_idx_d, swt_tab_idx_d)       (modbus_dev_table_s[dev_tab_idx_d].data_table->switch_table[swt_tab_idx_d].name)
#define swt_table_address(dev_tab_idx_d, swt_tab_idx_d)    (modbus_dev_table_s[dev_tab_idx_d].data_table->switch_table[swt_tab_idx_d].address)
#define swt_table_data(dev_tab_idx_d, swt_tab_idx_d)       (*modbus_dev_table_s[dev_tab_idx_d].data_table->switch_table[swt_tab_idx_d].data)
#define swt_table_write_mask(dev_tab_idx_d, swt_tab_idx_d) (modbus_dev_table_s[dev_tab_idx_d].data_table->switch_table[swt_tab_idx_d].write_mask)

#define reg_table_name(dev_tab_idx_d, reg_tab_idx_d)       (modbus_dev_table_s[dev_tab_idx_d].data_table->register_table[reg_tab_idx_d].name)
#define reg_table_address(dev_tab_idx_d, reg_tab_idx_d)    (modbus_dev_table_s[dev_tab_idx_d].data_table->register_table[reg_tab_idx_d].address)
#define reg_table_data(dev_tab_idx_d, reg_tab_idx_d)       (*modbus_dev_table_s[dev_tab_idx_d].data_table->register_table[reg_tab_idx_d].data)
#define reg_table_write_mask(dev_tab_idx_d, reg_tab_idx_d) (modbus_dev_table_s[dev_tab_idx_d].data_table->register_table[reg_tab_idx_d].write_mask)

/***************************************************************************************************
 * ***********************************获取设备表数据宏定义结束************************************ *
 ***************************************************************************************************/

#define MODBUS_FRAME_ADDRESS_BYTES_OCCUPIED                (1)  // 地址域所占用给字节
#define MODBUS_FRAME_FUNCTION_CODE_BYTES_OCCUPIED          (1)  // 功能码所占用字节
#define MODBUS_FRAME_DATA_START_ADDRESS_BYTES_OCCUPIED     (2)  // 数据起始地址所占用字节
#define MODBUS_FRAME_WRITE_ONE_DATA_BYTE_OCCUPIED          (2)  // 接收到写入一个数据指令，数据所占byte大小
#define MODBUS_FRAME_DATA_LEN_BYTES_OCCUPIED               (2)  // 数据长度所占用字节
#define MODBUS_FRAME_CRC_BYTES_OCCUPIED                    (2)  // CRC所占用字节
#define MODBUS_FRAME_WRITE_DATA_BYTE_NUMBER_BYTES_OCCUPIED (1)  // 需要写入多个数据的字节数量位所占用字节
// MODBUS接收一帧数据最小数据长度
#define MODBUS_ONE_FRAME_MIN_DATA_LEN                                                                                                 \
  (MODBUS_FRAME_ADDRESS_BYTES_OCCUPIED + MODBUS_FRAME_FUNCTION_CODE_BYTES_OCCUPIED + MODBUS_FRAME_DATA_START_ADDRESS_BYTES_OCCUPIED + \
   MODBUS_FRAME_DATA_LEN_BYTES_OCCUPIED + MODBUS_FRAME_CRC_BYTES_OCCUPIED)
#define MODBUS_WRITE_ONE_BYTE_DATA_FRAME_LEN                                                                                          \
  (MODBUS_FRAME_ADDRESS_BYTES_OCCUPIED + MODBUS_FRAME_FUNCTION_CODE_BYTES_OCCUPIED + MODBUS_FRAME_DATA_START_ADDRESS_BYTES_OCCUPIED + \
   MODBUS_FRAME_WRITE_ONE_DATA_BYTE_OCCUPIED + MODBUS_FRAME_CRC_BYTES_OCCUPIED)
#define MODBUS_ONE_FRAME_WRITE_MULTIPLE_DATA_MIN_DATA_LEN                                                                             \
  (MODBUS_FRAME_ADDRESS_BYTES_OCCUPIED + MODBUS_FRAME_FUNCTION_CODE_BYTES_OCCUPIED + MODBUS_FRAME_DATA_START_ADDRESS_BYTES_OCCUPIED + \
   MODBUS_FRAME_DATA_LEN_BYTES_OCCUPIED + MODBUS_FRAME_WRITE_DATA_BYTE_NUMBER_BYTES_OCCUPIED + MODBUS_FRAME_CRC_BYTES_OCCUPIED)
#define MODBUS_FUNCTION_CODE_ERROR (0X80)  // 当有错误码时，回执的功能码需要与上该参数，表示异常
#define ONE_BYTE_MAX_BIT           8       // 1个字节8位
#define temp_bit(index)            (temp.byte[0].bit._##index)
#define temp_bit_remainder_set_process_read(bit)                \
  ({                                                            \
    if ((data_index_interval % ONE_BYTE_MAX_BIT) > bit) {       \
      temp_bit(bit) = swt_table_data(dev_tab_idx, index + bit); \
    }                                                           \
  })
#define temp_bit_remainder_set_process_write(bit)                     \
  ({                                                                  \
    if ((data_index_interval % ONE_BYTE_MAX_BIT) > bit) {             \
      swt_table_data(dev_tab_idx, dat_tab_idx + bit) = temp_bit(bit); \
    }                                                                 \
  })
#define data_addr_check_and_tab_idx_get_d(table, size) \
  ({                                                   \
    for (size_t index = 0; index < size; index++) {    \
      if (table[index].address == data_address) {      \
        *data_tab_idx = index;                         \
        ret           = true;                          \
        break;                                         \
      }                                                \
    }                                                  \
  })
// MODBUS一帧数据长度(只剩下数据,出去地址域，功能码，起始寄存器地址，数据长度以及CRC)
/***************************************************************************************************
 * ***********************************通用的数据处理结构体开始************************************ *
 ***************************************************************************************************/
#define WORD_16_L_BYTE 0
#define WORD_16_H_BYTE 1

typedef struct {
  uint8_t _0 : 1;
  uint8_t _1 : 1;
  uint8_t _2 : 1;
  uint8_t _3 : 1;
  uint8_t _4 : 1;
  uint8_t _5 : 1;
  uint8_t _6 : 1;
  uint8_t _7 : 1;
} _8bit;

typedef union {
  uint8_t data;
  _8bit   bit;
} uint8_bit;

typedef union {
  uint8_bit byte[8];
  uint16_t  word_16;
  uint32_t  word_32;
  uint64_t  word_64;
} typ_iow;
/***************************************************************************************************
 * ***********************************通用的数据处理结构体结束************************************ *
 ***************************************************************************************************/
/* MODBUS使用的功能码 */
typedef enum {
  function_code_switch_read              = 0X01, /* 读开关,读取一路或多路开关的状态 (遥信) */
  function_code_register_read            = 0X03, /* 读寄存器,读取一个或多个寄存器 (模拟量) 数据 (遥测) */
  function_code_single_switch_write      = 0X05, /* 写单路开关,控制一路开关的分或合 (遥控) */
  function_code_single_register_write    = 0X06, /* 写单个寄存器,写入一个寄存器/模拟量数据 (遥调) */
  function_code_multiplex_switch_write   = 0X0F, /* 写多路开关,控制多路开关的分或合 (对多路开关同时遥控) */
  function_code_multiplex_register_write = 0X10, /* 写多个寄存器,写入多个寄存器/模拟量数据 (对多个寄存器同时遥调) */
} modbus_function_code_t;

/* MODBUS使用的错误码 */
typedef enum {
  /* 所有错误码都在回执后清除 */
  error_code_nomal                      = 0X00, /* 无错误 */
  error_code_invalid_message_type       = 0X01, /* 无效报文类型，该错误仅在receive_process中处理 */
  error_code_invalid_data_address       = 0X02, /* 无效的数据地址，包含数据长度越界 */
  error_code_invalid_data_value_written = 0X03, /* 写入的数据值无效 */
  error_code_device_busy                = 0X06, /* 装置忙 */
} modbus_error_code_t;

typedef enum {
  data_table_type_null     = 0X00, /* 空 */
  data_table_type_switch   = 0X01, /* 开关量表 */
  data_table_type_register = 0X02, /* 寄存器表 */
} data_table_type_t;

typedef enum {
  swt_open  = 0XFF00,  // 合闸(true)
  swt_close = 0X0000,  // 分闸(false)
} swt_write_one_byte_cmd_t;

/***************************************************************************************************
 * *****************************************static value****************************************** *
 ***************************************************************************************************/
static modbus_dev_table_t* modbus_dev_table_s;
static uint8_t             modbus_dev_table_len_s;
static uint8_t             modbus_send_len_s;   /* 发送长度，只有在数据接收需要回执时候才会设置 */
static modbus_error_code_t modbus_error_code_s; /* 错误码，所有错误码都在回执后清除 */

/***************************************************************************************************
 * ******************************************static func****************************************** *
 ***************************************************************************************************/
/*
MODBUS-CRC校验算法说明
1.预置1个16位的寄存器为十六进制的FFFF（即全为1）；称此寄存器为CRC寄存器。
2.把第一个8位二进制数据（即通讯信息帧的第一个字节）与16位的CRC寄存器的低 8位相异或，把结果存放在CRC寄存器。
3.把CRC寄存器的内容右移一位（朝低位）用0填补最高位，并检查右移后的移出位。
4.如果移出位为0：重复第3步（再次右移1位）；如果移出位为1：CRC寄存器与多项式A001（1010 0000 0000 0001）进行异或。
5.重复步骤3和4，直到右移8次，这样整个8位数据全部进行了处理。
6.重复步骤2到步骤5，进行通讯信息帧下一个字节的处理。
7.将通讯信息帧的所有字节按上述步骤计算完成后，得到16位CRC寄存器的高，低字节交换。
8.最后得到的CRC寄存器内容即为：CRC码。
 */
static unsigned short modbus_crc16(const void* data, int data_len) {
  unsigned short crc_reg = 0xffff;  // crc寄存器
  for (int index = 0; index < data_len; index++) {
    unsigned short data_byte = (((unsigned char*)data)[index]);
    for (char bit_cnt = 0; bit_cnt < 8; bit_cnt++) {
      crc_reg = ((data_byte ^ crc_reg) & 1) ? (crc_reg >> 1) ^ 0xA001 : (crc_reg >> 1);
      data_byte >>= 1;
    }
  }
  return (crc_reg << 8) | (crc_reg >> 8);
}

static void modbus_dev_table_switch_dump(uint8_t index) {
  for (size_t i = 0; i < modbus_dev_table_s[index].data_table->switch_table_len; i++) {
    printf("name:%s,address:%d,data:0X%x,write_mask:%d\n",  //
           swt_table_name(index, i),                        //
           swt_table_address(index, i),                     //
           swt_table_data(index, i),                        //
           swt_table_write_mask(index, i));
  }
}

static void modbus_dev_table_register_dump(uint8_t index) {
  for (size_t i = 0; i < modbus_dev_table_s[index].data_table->register_table_len; i++) {
    printf("name:%s,address:%d,data:0X%x,write_mask:%d\n",  //
           reg_table_name(index, i),                        //
           reg_table_address(index, i),                     //
           reg_table_data(index, i),                        //
           reg_table_write_mask(index, i));
  }
}

static void modbus_dev_table_dump(void) {
  for (size_t index = 0; index < modbus_dev_table_len_s; index++) {
    printf("device_address:%d\n", modbus_dev_table_s[index].device_address);
    printf("send_buffer_len:%d\n", modbus_dev_table_s[index].send_buffer_len);
    modbus_dev_table_switch_dump(index);
    modbus_dev_table_register_dump(index);
    printf("frame_data_max_len:%d\n", modbus_dev_table_s[index].frame_data_max_len);
  }
}

static uint8_t  get_device_address_from_data(uint8_t* data) { return data[0]; }
static uint8_t  get_function_code_from_data(uint8_t* data) { return data[1]; }
static uint16_t get_data_start_address_from_data(uint8_t* data) { return ((data[2] << 8) | (data[3] << 0)); }
static uint16_t get_data_len_from_data(uint8_t* data) { return ((data[4] << 8) | (data[5] << 0)); }
// 写入单个数据时，4和5不是数据长度位了，是要写入的数据
static uint16_t get_data_function_write_one_byte_from_data(uint8_t* data) { return ((data[4] << 8) | (data[5] << 0)); }
static uint8_t  get_write_multiplex_byte_from_data(uint8_t* data) { return data[6]; }

/**
 * @brief   设备地址校验，并获取数据表下标
 * @param   uint8_t  device_address   设备地址
 * @param   uint8_t* dev_table_index  设备表数组下标
 * @retval  bool true校验成功，false校验失败
 * @note
 */
static bool dev_addr_check_and_tab_idx_get(uint8_t device_address, uint8_t* dev_table_index) {
  for (size_t index = 0; index < modbus_dev_table_len_s; index++) {
    if (dev_table_device_address(index) == device_address) {
      *dev_table_index = index;
      return true;
    }
  }
  return false;
}

/**
 * @brief   数据地址校验，并获取数据表数组下标
 * @param   uint8_t           dev_table_index  设备表数组下标
 * @param   uint16_t          data_address     数据地址
 * @param   data_table_type_t data_table_type  数据表类型
 * @param   uint16_t*         data_tab_idx     数据表数组下标
 * @retval  bool true校验成功，false校验失败
 * @note
 */
static bool data_addr_check_and_tab_idx_get(uint8_t           dev_table_index,  //
                                            uint16_t          data_address,     //
                                            data_table_type_t data_table_type,  //
                                            uint16_t*         data_tab_idx) {
  bool ret = false;
  if (data_table_type == data_table_type_switch) {
    data_addr_check_and_tab_idx_get_d(data_table_switch_table(dev_table_index),  //
                                      data_table_switch_table_size(dev_table_index));
  } else if (data_table_type == data_table_type_register) {
    data_addr_check_and_tab_idx_get_d(data_table_register_table(dev_table_index),  //
                                      data_table_register_table_size(dev_table_index));
  }
  return ret;
}

/**
 * @brief   crc校验
 * @param   uint8_t* data     接收到的数据
 * @param   uint8_t data_len  接收到的数据长度
 * @retval  bool true校验成功，false校验失败
 * @note
 */
static bool modbus_crc16_check(uint8_t* data, uint8_t data_len) {
  /* 注意：传入的数据包括crc */
  typ_iow temp;
  temp.word_16 = modbus_crc16(data, data_len - 2);
  if ((temp.byte[0].data == data[data_len - 1]) && (temp.byte[1].data == data[data_len - 2])) return true;
  return false;
}

/**
 * @brief   读取多个开关量指令处理
 * @param   uint8_t* data                    接收到的数据
 * @param   uint8_t data_len                 接收到的数据长度
 * @param   uint8_t dev_tab_idx              设备表数组下标
 * @param   int32_t data_tab_start_addr_idx  数据表处理的起始地址
 * @retval  void
 * @note
 */
static void function_code_switch_read_process(uint8_t* data, uint8_t data_len, uint8_t dev_tab_idx, int32_t data_tab_start_addr_idx) {
  int16_t  data_table_end_index = 0;
  int16_t  data_index_interval  = 0;
  uint16_t send_buffer_index    = 0;
  typ_iow  temp;

  // 设置地址域
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = dev_table_device_address(dev_tab_idx);
  // 设置功能码
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = function_code_switch_read;
  // 设置回执数据长度,/8是因为开关量表一个地址数据代表1位，回执是按字节算长度的
  // 由于在data_address_check中校验过了，所以直接*2就行了，不需要判断sendbuffer够不够
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] =
      ((get_data_len_from_data(data) % 8) == 0) ? get_data_len_from_data(data) / 8 : get_data_len_from_data(data) / 8 + 1;
  data_table_end_index = data_tab_start_addr_idx + get_data_len_from_data(data);
  for (size_t index = data_tab_start_addr_idx; index < data_table_end_index; index += 8) {
    temp.byte[0].data   = 0;                             // 清空数据
    data_index_interval = data_table_end_index - index;  // 数据间隔，表示还有多少个数据需要读取
    if ((data_index_interval / ONE_BYTE_MAX_BIT) > 0) {  // 如果大于8个数据
      temp_bit(0) = swt_table_data(dev_tab_idx, index + 0);
      temp_bit(1) = swt_table_data(dev_tab_idx, index + 1);
      temp_bit(2) = swt_table_data(dev_tab_idx, index + 2);
      temp_bit(3) = swt_table_data(dev_tab_idx, index + 3);
      temp_bit(4) = swt_table_data(dev_tab_idx, index + 4);
      temp_bit(5) = swt_table_data(dev_tab_idx, index + 5);
      temp_bit(6) = swt_table_data(dev_tab_idx, index + 6);
      temp_bit(7) = swt_table_data(dev_tab_idx, index + 7);
    } else {  // 小于8个数据
      // 没有对bit7处理是因为如果有了bit7，就不会进入此处，在上面就处理了
      // 此处将数据位的余数进行设置处理，没有读的数据，全返回0
      temp_bit_remainder_set_process_read(0);
      temp_bit_remainder_set_process_read(1);
      temp_bit_remainder_set_process_read(2);
      temp_bit_remainder_set_process_read(3);
      temp_bit_remainder_set_process_read(4);
      temp_bit_remainder_set_process_read(5);
      temp_bit_remainder_set_process_read(6);
    }

    dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[0].data;
  }
  // 第三位为数据长度，+3是加上地址域，功能码，数据长度位
  temp.word_16 = modbus_crc16(dev_table_send_buffer(dev_tab_idx), (dev_table_send_buffer(dev_tab_idx)[2] + 3));

  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_H_BYTE].data;
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_L_BYTE].data;
  modbus_send_len_s                                       = send_buffer_index;
}

/**
 * @brief   读取多个寄存器指令处理
 * @param   uint8_t* data                    接收到的数据
 * @param   uint8_t data_len                 接收到的数据长度
 * @param   uint8_t dev_tab_idx              设备表数组下标
 * @param   int32_t data_tab_start_addr_idx  数据表处理的起始地址
 * @retval  void
 * @note
 */
static void function_code_register_read_process(uint8_t* data, uint8_t data_len, uint8_t dev_tab_idx, int32_t data_tab_start_addr_idx) {
  uint16_t send_buffer_index = 0;
  typ_iow  temp;

  // 设置地址域
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = dev_table_device_address(dev_tab_idx);
  // 设置功能码
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = function_code_register_read;
  // 设置回执数据长度,*2是因为读取的一个数据是2字节，回执是按字节算长度的
  // 由于在data_address_check中校验过了，所以直接*2就行了，不需要判断sendbuffer够不够
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = get_data_len_from_data(data) * 2;
  for (size_t index = data_tab_start_addr_idx; index < (data_tab_start_addr_idx + get_data_len_from_data(data)); index++) {
    temp.word_16                                            = reg_table_data(dev_tab_idx, index);
    dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_H_BYTE].data;
    dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_L_BYTE].data;
  }
  // 第三位为数据长度，+3是加上地址域，功能码，数据长度位
  temp.word_16 = modbus_crc16(dev_table_send_buffer(dev_tab_idx), (dev_table_send_buffer(dev_tab_idx)[2] + 3));

  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_H_BYTE].data;
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_L_BYTE].data;
  modbus_send_len_s                                       = send_buffer_index;
}

/**
 * @brief   写单个开关量指令处理
 * @param   uint8_t* data                    接收到的数据
 * @param   uint8_t data_len                 接收到的数据长度
 * @param   uint8_t dev_tab_idx              设备表数组下标
 * @param   int32_t data_tab_start_addr_idx  数据表处理的起始地址
 * @retval  void
 * @note
 */
static void function_code_single_switch_write_process(uint8_t* data, uint8_t data_len, uint8_t dev_tab_idx, int32_t data_tab_start_addr_idx) {
  uint16_t send_buffer_index = 0;
  typ_iow  temp;
  if (data_len != MODBUS_WRITE_ONE_BYTE_DATA_FRAME_LEN) {
    // 写入一个数据的指令长度只能为8
    modbus_error_code_s = error_code_invalid_data_address;
    return;
  }
  if (!reg_table_write_mask(dev_tab_idx, data_tab_start_addr_idx) ||
      !((get_data_function_write_one_byte_from_data(data) == swt_open) || (get_data_function_write_one_byte_from_data(data) == swt_close))) {
    modbus_error_code_s = error_code_invalid_data_value_written;
    return;
  }

  // 数据写入
  if (get_data_function_write_one_byte_from_data(data) == swt_open) {
    swt_table_data(dev_tab_idx, data_tab_start_addr_idx) = true;
  } else {  // 由于前面判断了数据只能为两种，所以不用else if
    swt_table_data(dev_tab_idx, data_tab_start_addr_idx) = false;
  }

  // 设置回执地址域
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = dev_table_device_address(dev_tab_idx);
  // 设置回执功能码
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = function_code_single_switch_write;
  // 设置回执写入的地址
  temp.word_16                                            = reg_table_address(dev_tab_idx, data_tab_start_addr_idx);
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_H_BYTE].data;
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_L_BYTE].data;
  // 设置回执写入地址的数据
  if (reg_table_data(dev_tab_idx, data_tab_start_addr_idx)) {
    temp.word_16 = swt_open;
  } else {
    temp.word_16 = swt_close;
  }
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_H_BYTE].data;
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_L_BYTE].data;
  // 设置回执的CRC校验
  temp.word_16 = modbus_crc16(dev_table_send_buffer(dev_tab_idx), (MODBUS_WRITE_ONE_BYTE_DATA_FRAME_LEN - MODBUS_FRAME_CRC_BYTES_OCCUPIED));
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_H_BYTE].data;
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_L_BYTE].data;

  modbus_send_len_s = send_buffer_index;
}

/**
 * @brief   写单个寄存器指令处理
 * @param   uint8_t* data                    接收到的数据
 * @param   uint8_t data_len                 接收到的数据长度
 * @param   uint8_t dev_tab_idx              设备表数组下标
 * @param   int32_t data_tab_start_addr_idx  数据表处理的起始地址
 * @retval  void
 * @note
 */
static void function_code_single_register_write_process(uint8_t* data, uint8_t data_len, uint8_t dev_tab_idx, int32_t data_tab_start_addr_idx) {
  uint16_t send_buffer_index = 0;
  typ_iow  temp;
  if (data_len != MODBUS_WRITE_ONE_BYTE_DATA_FRAME_LEN) {
    // 写入一个数据的指令长度只能为8
    modbus_error_code_s = error_code_invalid_data_address;
    return;
  }
  if (!reg_table_write_mask(dev_tab_idx, data_tab_start_addr_idx)) {
    modbus_error_code_s = error_code_invalid_data_value_written;
    return;
  }

  reg_table_data(dev_tab_idx, data_tab_start_addr_idx) = get_data_function_write_one_byte_from_data(data);

  // 设置回执地址域
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = dev_table_device_address(dev_tab_idx);
  // 设置回执功能码
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = function_code_single_register_write;
  // 设置回执写入的地址
  temp.word_16                                            = reg_table_address(dev_tab_idx, data_tab_start_addr_idx);
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_H_BYTE].data;
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_L_BYTE].data;
  // 设置回执写入地址的数据
  temp.word_16                                            = reg_table_data(dev_tab_idx, data_tab_start_addr_idx);
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_H_BYTE].data;
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_L_BYTE].data;
  // 设置回执的CRC校验
  temp.word_16 = modbus_crc16(dev_table_send_buffer(dev_tab_idx), (MODBUS_WRITE_ONE_BYTE_DATA_FRAME_LEN - MODBUS_FRAME_CRC_BYTES_OCCUPIED));
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_H_BYTE].data;
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_L_BYTE].data;

  modbus_send_len_s = send_buffer_index;
}

/**
 * @brief   写多个开关量指令处理
 * @param   uint8_t* data                    接收到的数据
 * @param   uint8_t data_len                 接收到的数据长度
 * @param   uint8_t dev_tab_idx              设备表数组下标
 * @param   int32_t data_tab_start_addr_idx  数据表处理的起始地址
 * @retval  void
 * @note
 */
static void function_code_multiplex_switch_write_process(uint8_t* data, uint8_t data_len, uint8_t dev_tab_idx, int32_t data_tab_start_addr_idx) {
  uint8_t  data_len_check_temp  = get_data_len_from_data(data);
  uint16_t send_buffer_index    = 0;
  int16_t  data_table_end_index = 0;
  int16_t  data_index_interval  = 0;
  size_t   data_byte_count      = 0;
  typ_iow  temp;

  if (data_len != (MODBUS_ONE_FRAME_WRITE_MULTIPLE_DATA_MIN_DATA_LEN + get_write_multiplex_byte_from_data(data))) {
    // 写入多个数据指令数据长度验证
    modbus_error_code_s = error_code_invalid_data_address;
    return;
  }

  if (data_len_check_temp % 8 == 0) {
    data_len_check_temp = data_len_check_temp / 8;
  } else {
    data_len_check_temp = data_len_check_temp / 8 + 1;
  }

  if (data_len_check_temp != get_write_multiplex_byte_from_data(data)) {
    modbus_error_code_s = error_code_invalid_data_value_written;
  }

  for (size_t index = 0; index < get_data_len_from_data(data); index++) {
    if (!swt_table_write_mask(dev_tab_idx, data_tab_start_addr_idx + index)) {
      // 只要有一个不让数据写入就退出
      modbus_error_code_s = error_code_invalid_data_value_written;
      return;
    }
  }

  data_table_end_index = data_tab_start_addr_idx + get_data_len_from_data(data);
  for (size_t dat_tab_idx = data_tab_start_addr_idx; dat_tab_idx < data_table_end_index; dat_tab_idx += 8) {
    temp.byte[0].data   = data[7 + data_byte_count];           // 获取data中的数据位
    data_index_interval = data_table_end_index - dat_tab_idx;  // 数据间隔，表示还有多少个数据需要读取
    if ((data_index_interval / ONE_BYTE_MAX_BIT) > 0) {        // 如果大于8个数据
      swt_table_data(dev_tab_idx, dat_tab_idx + 0) = temp.byte[0].bit._0;
      swt_table_data(dev_tab_idx, dat_tab_idx + 1) = temp.byte[0].bit._1;
      swt_table_data(dev_tab_idx, dat_tab_idx + 2) = temp.byte[0].bit._2;
      swt_table_data(dev_tab_idx, dat_tab_idx + 3) = temp.byte[0].bit._3;
      swt_table_data(dev_tab_idx, dat_tab_idx + 4) = temp.byte[0].bit._4;
      swt_table_data(dev_tab_idx, dat_tab_idx + 5) = temp.byte[0].bit._5;
      swt_table_data(dev_tab_idx, dat_tab_idx + 6) = temp.byte[0].bit._6;
      swt_table_data(dev_tab_idx, dat_tab_idx + 7) = temp.byte[0].bit._7;
    } else {  // 如果小于8个数据
      // 没有对bit7处理是因为如果有了bit7，就不会进入此处，在上面就处理了
      // 此处将数据位的余数进行设置处理
      temp_bit_remainder_set_process_write(0);
      temp_bit_remainder_set_process_write(1);
      temp_bit_remainder_set_process_write(2);
      temp_bit_remainder_set_process_write(3);
      temp_bit_remainder_set_process_write(4);
      temp_bit_remainder_set_process_write(5);
      temp_bit_remainder_set_process_write(6);
    }
    data_byte_count++;
  }

  // 设置地址域
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = dev_table_device_address(dev_tab_idx);
  // 设置功能码
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = function_code_multiplex_switch_write;
  // 设置写入的起始地址
  temp.word_16                                            = data_tab_start_addr_idx;
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_H_BYTE].data;
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_L_BYTE].data;
  // 设置写入的数据长度
  temp.word_16                                            = get_data_len_from_data(data);
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_H_BYTE].data;
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_L_BYTE].data;
  // 设置CRC
  temp.word_16 = modbus_crc16(dev_table_send_buffer(dev_tab_idx), (MODBUS_WRITE_ONE_BYTE_DATA_FRAME_LEN - MODBUS_FRAME_CRC_BYTES_OCCUPIED));
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_H_BYTE].data;
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_L_BYTE].data;

  modbus_send_len_s = send_buffer_index;
}

/**
 * @brief   写多个寄存器指令处理
 * @param   uint8_t* data                    接收到的数据
 * @param   uint8_t data_len                 接收到的数据长度
 * @param   uint8_t dev_tab_idx              设备表数组下标
 * @param   int32_t data_tab_start_addr_idx  数据表处理的起始地址
 * @retval  void
 * @note
 */
static void function_code_multiplex_register_write_process(uint8_t* data, uint8_t data_len, uint8_t dev_tab_idx, int32_t data_tab_start_addr_idx) {
  uint16_t send_buffer_index = 0;
  typ_iow  temp;

  if (data_len != (MODBUS_ONE_FRAME_WRITE_MULTIPLE_DATA_MIN_DATA_LEN + get_write_multiplex_byte_from_data(data))) {
    // 写入多个数据指令数据长度验证
    modbus_error_code_s = error_code_invalid_data_address;
    return;
  }

  for (size_t index = 0; index < get_data_len_from_data(data); index++) {
    if (!reg_table_write_mask(dev_tab_idx, data_tab_start_addr_idx + index)) {
      // 只要有一个不让数据写入就退出
      modbus_error_code_s = error_code_invalid_data_value_written;
      return;
    }
  }

  for (size_t index = 0; index < get_data_len_from_data(data); index++) {
    // 写入数据，与错误码判断分开是为了防止要写入的数据中间不让写入，但是前面部分又写了。
    temp.byte[WORD_16_L_BYTE].data                               = data[8 + (index * 2)];
    temp.byte[WORD_16_H_BYTE].data                               = data[7 + (index * 2)];
    reg_table_data(dev_tab_idx, data_tab_start_addr_idx + index) = temp.word_16;
  }

  // 设置回执地址域
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = dev_table_device_address(dev_tab_idx);
  // 设置回执功能码
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = function_code_multiplex_register_write;
  // 设置回执写入的地址
  temp.word_16                                            = reg_table_address(dev_tab_idx, data_tab_start_addr_idx);
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_H_BYTE].data;
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_L_BYTE].data;
  // 设置回执写入了多少个数据
  temp.word_16                                            = get_data_len_from_data(data);
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_H_BYTE].data;
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_L_BYTE].data;
  // 设置回执的CRC校验
  temp.word_16 = modbus_crc16(dev_table_send_buffer(dev_tab_idx), (MODBUS_WRITE_ONE_BYTE_DATA_FRAME_LEN - MODBUS_FRAME_CRC_BYTES_OCCUPIED));
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_H_BYTE].data;
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_L_BYTE].data;

  modbus_send_len_s = send_buffer_index;
}

/**
 * @brief   遍历数据表并校验数据地址
 * @param   uint8_t           dev_tab_idx              设备表数组下标
 * @param   data_table_type_t data_table_type          数据表类型
 * @param   uint8_t*          data                     接收到的数据
 * @param   uint8_t*          count                    计数获取
 * @param   int32_t*          data_tab_start_addr_idx  数据表的起始地址
 * @retval  void
 * @note
 */
static void data_table_traversing_check_data_addr(uint8_t           dev_tab_idx,             // 设备表索引
                                                  data_table_type_t data_table_type,         // 数据表类型
                                                  uint8_t*          data,                    // 接收到的数据
                                                  uint8_t*          count,                   // 地址相同的计数
                                                  int32_t*          data_tab_start_addr_idx  // 数据表起始地址数组下标
) {
  uint16_t data_len     = get_data_len_from_data(data);            // 需要比对的数据长度
  uint16_t data_address = get_data_start_address_from_data(data);  // 需要比对的数据起始地址

  if (data_table_type == data_table_type_switch) {
    for (size_t i = 0; i < data_table_switch_table_size(dev_tab_idx); i++) {  // 遍历表
      if (swt_table_address(dev_tab_idx, i) == data_address) {                // 比对地址是否相同
        if (*count == 0) *data_tab_start_addr_idx = i;                        // 获取数据位首地址数组下标
        *count = *count + 1;                                                  // 计数+1
        // 计数与数据长度相同，则直接退出，不可能会出现超出计数的情况，只会出现计数少的情况
        if (get_data_len_from_data(data) == *count) break;
        // data_address指的是下一个需要比较的地址
        if (data_len > 0) data_len--;
        if (data_len > 0) data_address++;
      }
    }
  } else if (data_table_type == data_table_type_register) {
    for (size_t i = 0; i < data_table_register_table_size(dev_tab_idx); i++) {  // 遍历表
      if (reg_table_address(dev_tab_idx, i) == data_address) {                  // 比对地址是否相同
        if (*count == 0) *data_tab_start_addr_idx = i;                          // 获取数据位首地址数组下标
        *count = *count + 1;                                                    // 计数+1
        // 计数与数据长度相同，则直接退出，不可能会出现超出计数的情况，只会出现计数少的情况
        if (get_data_len_from_data(data) == *count) break;
        // data_address指的是下一个需要比较的地址
        if (data_len > 0) data_len--;
        if (data_len > 0) data_address++;
      }
    }
  }
}

/**
 * @brief   数据表遍历校验地址间隔
 * @param   uint8_t           dev_tab_idx              设备表数组下标
 * @param   data_table_type_t data_table_type          数据表类型
 * @param   uint8_t*          data                     接收到的数据
 * @param   uint8_t*          count                    计数获取
 * @param   int32_t*          data_tab_start_addr_idx  数据表的起始地址
 * @retval  void
 * @note
 */
static void data_table_traversing_check_individual_data_addr(uint8_t           dev_tab_idx,             // 设备表索引
                                                             data_table_type_t data_table_type,         // 数据表类型
                                                             uint8_t*          data,                    // 接收到的数据
                                                             uint8_t*          count,                   // 地址相同的计数
                                                             int32_t*          data_tab_start_addr_idx  // 数据表起始地址数组下标
) {
  uint16_t data_address = get_data_start_address_from_data(data);  // 需要比对的数据起始地址
  if (data_table_type == data_table_type_switch) {
    for (size_t i = 0; i < data_table_switch_table_size(dev_tab_idx); i++) {  // 遍历表
      if (swt_table_address(dev_tab_idx, i) == data_address) {                // 比对地址是否相同
        *count = *count + 1;
        break;
      }
    }
  } else if (data_table_type == data_table_type_register) {
    for (size_t i = 0; i < data_table_register_table_size(dev_tab_idx); i++) {  // 遍历表
      if (reg_table_address(dev_tab_idx, i) == data_address) {                  // 比对地址是否相同
        *count = *count + 1;
        break;
      }
    }
  }
}

/**
 * @brief   数据地址校验
 * @param   uint8_t*          data                     接收到的数据
 * @param   uint8_t           data_len                 接收到的数据长度
 * @param   uint8_t           dev_tab_idx              设备表数组下标
 * @param   data_table_type_t data_table_type          数据表类型
 * @param   int32_t*          data_tab_start_addr_idx  获取数据表数据的起始地址
 * @retval  void
 * @note
 */
static bool data_address_check(uint8_t*          data,             //
                               uint8_t           data_len,         //
                               uint8_t           dev_tab_idx,      //
                               data_table_type_t data_table_type,  //
                               int32_t*          data_tab_start_addr_idx) {
  /*
   * 数据地址检测
   * 1.无效的数据地址，包含数据长度越界
   * 2.校验成功返回true，校验失败返回false
   */
  bool    data_length_check_flag  = false;
  bool    data_address_check_flag = false;
  uint8_t count                   = 0;
  // 用户设置的一帧最大数据减去MODBUS最小数据，除2是因为一个数据占用2个字节。
  uint16_t frame_max_data_len = ((dev_table_frame_data_max_len(dev_tab_idx) - MODBUS_ONE_FRAME_MIN_DATA_LEN) / 2);
  // 发送buffer最大数据
  uint16_t send_max_data_len = ((dev_table_send_buffer_len(dev_tab_idx) - MODBUS_ONE_FRAME_MIN_DATA_LEN) / 2);
  /* 因为data_table_type外面判断了data_table_type_null，如果为null就不会进来，所以不处理null */
  /* 验证数据地址 */
  if ((get_function_code_from_data(data) == function_code_switch_read) ||             //
      (get_function_code_from_data(data) == function_code_register_read) ||           //
      (get_function_code_from_data(data) == function_code_multiplex_switch_write) ||  //
      (get_function_code_from_data(data) == function_code_multiplex_register_write)) {
    data_table_traversing_check_data_addr(dev_tab_idx, data_table_type, data, &count, data_tab_start_addr_idx);
    if (count == get_data_len_from_data(data)) {
      /* 计数与数据长度相同，说明所有data_address都存在 */
      data_address_check_flag = true;
    }
    if ((data_len <= frame_max_data_len) &&                     // 接收的数据长度没超出一帧最大值
        (dev_table_send_buffer(dev_tab_idx) != NULL) &&         // 发送buffer不为空
        (get_data_len_from_data(data) <= send_max_data_len)) {  // 需要回执的数据组包，防止超出发送buffer长度
      /* 数据长度如果没超出 */
      data_length_check_flag = true;
    }
  } else if ((get_function_code_from_data(data) == function_code_single_switch_write) ||  //
             (get_function_code_from_data(data) == function_code_single_register_write)) {
    data_table_traversing_check_individual_data_addr(dev_tab_idx, data_table_type, data, &count, data_tab_start_addr_idx);
    if (count == 1) {
      /* 因为只写一个，所以count判断==1即可 */
      data_address_check_flag = true;
    }
    if ((data_len <= frame_max_data_len) &&              // 接收的数据长度没超出一帧最大值
        (dev_table_send_buffer(dev_tab_idx) != NULL) &&  // 发送buffer不为空
        (data_len <= send_max_data_len)) {               // 需要回执的数据组包，防止超出发送buffer长度
      /* 数据长度如果没超出 */
      data_length_check_flag = true;
    }
  }

  if (data_length_check_flag && data_address_check_flag) {
    /* 两个标志都为true才校验成功 */
    return true;
  }

  return false;
}

/**
 * @brief   接收处理函数
 * @param   uint8_t* data                      接收到的数据
 * @param   uint8_t data_len                   接收到的数据长度
 * @param   uint8_t dev_tab_idx                设备表数组下标
 * @param   data_table_type_t data_table_type  需要操作的数据表类型
 * @retval  void
 * @note    针对不同指令的数据进行处理，并组包。
 */
static void receive_process(uint8_t* data, uint8_t data_len, uint8_t dev_tab_idx, data_table_type_t data_table_type) {
  int32_t data_tab_start_addr_idx = 0;
  if (!data_address_check(data, data_len, dev_tab_idx, data_table_type, &data_tab_start_addr_idx)) {
    modbus_error_code_s = error_code_invalid_data_address;
    return;
  }
  switch (get_function_code_from_data(data)) {
    case function_code_switch_read:
      function_code_switch_read_process(data, data_len, dev_tab_idx, data_tab_start_addr_idx);
      break;
    case function_code_register_read:
      function_code_register_read_process(data, data_len, dev_tab_idx, data_tab_start_addr_idx);
      break;
    case function_code_single_switch_write:
      function_code_single_switch_write_process(data, data_len, dev_tab_idx, data_tab_start_addr_idx);
      break;
    case function_code_single_register_write:
      function_code_single_register_write_process(data, data_len, dev_tab_idx, data_tab_start_addr_idx);
      break;
    case function_code_multiplex_switch_write:
      function_code_multiplex_switch_write_process(data, data_len, dev_tab_idx, data_tab_start_addr_idx);
      break;
    case function_code_multiplex_register_write:
      function_code_multiplex_register_write_process(data, data_len, dev_tab_idx, data_tab_start_addr_idx);
      break;
  }
}

/**
 * @brief   根据接收到的数据获取数据表类型
 * @param   uint8_t* data         接收到的数据
 * @retval  data_table_type_t     数据表类型
 * @note
 */
static data_table_type_t get_data_table_type_from_data(uint8_t* data) {
  /* 获取当前指令使用的是哪个数据表(开关量表和寄存器表) */
  uint8_t function_code = get_function_code_from_data(data);

  if ((function_code == function_code_switch_read) ||          //
      (function_code == function_code_single_switch_write) ||  //
      (function_code == function_code_multiplex_switch_write)) {
    return data_table_type_switch;
  } else if ((function_code == function_code_register_read) ||          //
             (function_code == function_code_single_register_write) ||  //
             (function_code == function_code_multiplex_register_write)) {
    return data_table_type_register;
  }
  return data_table_type_null;
}

/**
 * @brief   错误上报
 * @param   uint8_t* data         错误上报的数据
 * @param   uint8_t  data_len     错误上报的数据长度
 * @param   uint8_t  dev_tab_idx  设备表数组下标
 * @retval  void
 * @note
 */
static void modbus_error_response(uint8_t* data, uint8_t data_len, uint8_t dev_tab_idx) {
  typ_iow  temp;
  uint16_t send_buffer_index                              = 0;
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = data[0];
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = data[1] | MODBUS_FUNCTION_CODE_ERROR;
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = modbus_error_code_s;

  // 错误回执只有3位数据，分别是地址域，功能码，错误码
  temp.word_16 = modbus_crc16(dev_table_send_buffer(dev_tab_idx), 3);

  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_H_BYTE].data;
  dev_table_send_buffer(dev_tab_idx)[send_buffer_index++] = temp.byte[WORD_16_L_BYTE].data;
  modbus_send_len_s                                       = send_buffer_index;
}

/***************************************************************************************************
 * ********************************************modbus********************************************* *
 ***************************************************************************************************/
/**
 * @brief   设备表注册
 * @param   modbus_dev_table_t* table 设备表
 * @param   uint8_t table_size        设备表大小
 * @retval  void
 * @note
 */
void modbus_dev_reg(modbus_dev_table_t* table, uint8_t table_size) {
  modbus_dev_table_s     = table;
  modbus_dev_table_len_s = table_size;
  // modbus_dev_table_dump();
}

/**
 * @brief   modbus接收接口
 * @param   uint8_t* data    接收到的数据
 * @param   uint8_t data_len 接收到的数据长度
 * @retval  int16_t 小于0表示报错，大于0表示回执字节长度
 * @note    接收后进行数据处理，组包。
 */
int16_t modbus_receive(uint8_t* data, uint8_t data_len) {
  /*
   * 读指令的帧格式
   * | 地址域 |   功能码  |  起始地址  |  数据长度 |  CRC校验  |
   * |  1byte |   1byte  |   2byte   |   2byte  |   2byte   |
   * 写指令的帧格式
   * | 地址域 |   功能码  |  起始地址  |  数据长度 |  需要写入的数据  |  CRC校验  |
   * |  1byte |   1byte  |   2byte   |   2byte  |       ...       |   2byte   |
   * 提示1：ADU就是完整的一帧数据
   * 提示2：PDU只包含功能码和数据
   */
  uint8_t           dev_tab_idx;  // 设备表数组下标
  data_table_type_t data_table_type = get_data_table_type_from_data(data);
  /* 校验数据是否存在，MODBUS数据读写，最少会传入八个字节数据 */
  if ((data == NULL) ||                              //
      (data_len < MODBUS_ONE_FRAME_MIN_DATA_LEN) ||  //
      (modbus_dev_table_s == NULL) ||                // 防止没init后使用导致代码崩溃
      (modbus_dev_table_len_s == 0))                 // 只要没有获取发送数据，就不处理新数据
    return -1;

  modbus_send_len_s = 0;

  /*
   * 1.校验设备地址是否存在
   * 2.CRC校验是否正确
   * 注意：接收到的报文或CRC校验错误则不回答
   */
  if (dev_addr_check_and_tab_idx_get(get_device_address_from_data(data), &dev_tab_idx) && modbus_crc16_check(data, data_len)) {
    if (data_table_type != data_table_type_null) {
      /* 数据组包在receive_process中进行组包 */
      receive_process(data, data_len, dev_tab_idx, data_table_type);
    } else {
      /* 设置错误码为无效的报文类型 */
      modbus_error_code_s = error_code_invalid_message_type;
    }

    if (modbus_error_code_s != error_code_nomal) {
      /* 发生错误，错误回执组包 */
      modbus_error_response(data, data_len, dev_tab_idx);
    }
    modbus_error_code_s = error_code_nomal; /* 错误码一定要清除，否则下次接收则直接报错 */
  } else {
    return -2;
  }
  return modbus_send_len_s;
}

/***************************************************************************************************
 * ********************************提供给外部查询数据表下标的接口********************************* *
 ***************************************************************************************************/
/**
 * @brief   根据设备域地址以及数据地址查询开关量表数组下标
 * @param   uint8_t device_address 需要查询的设备表地址
 * @param   uint16_t data_address  需要查询的数据表地址
 * @retval  int32_t 找到的数据表数组下标或错误，-1表示没找到设备表地址，-2表示没找到数据表地址
 * @note
 */
int32_t modbus_find_switch_table_index_from_data_address(uint8_t device_address, uint16_t data_address) {
  uint8_t  dev_table_index    = 0;
  uint16_t switch_table_index = 0;
  if (!dev_addr_check_and_tab_idx_get(device_address, &dev_table_index)) return -1;
  if (!data_addr_check_and_tab_idx_get(dev_table_index,         //
                                       data_address,            //
                                       data_table_type_switch,  //
                                       &switch_table_index)) {
    return -2;
  }
  return switch_table_index;
}
/**
 * @brief   根据设备域地址以及数据地址查询寄存器表数组下标
 * @param   uint8_t device_address 需要查询的设备表地址
 * @param   uint16_t data_address  需要查询的数据表地址
 * @retval  int32_t 找到的数据表数组下标或错误，-1表示没找到设备表地址，-2表示没找到数据表地址
 * @note
 */
int32_t modbus_find_register_table_index_from_data_address(uint8_t device_address, uint16_t data_address) {
  uint8_t  dev_table_index      = 0;
  uint16_t register_table_index = 0;
  if (!dev_addr_check_and_tab_idx_get(device_address, &dev_table_index)) return -1;
  if (!data_addr_check_and_tab_idx_get(dev_table_index,           //
                                       data_address,              //
                                       data_table_type_register,  //
                                       &register_table_index)) {
    return -2;
  }
  return register_table_index;
}

/**
 * @brief   获取软件发行版本
 * @retval  int32_t 返回的软件发行版本
 * @note
 */
uint32_t modbus_soft_release_version_get(void) { return TMODBUS_SOFT_RELEASE_VERSION; }